using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public static class XilinxTools
    {
        static System.IO.TextWriter twXSTScript;
        static string sUCF = "";


        public static void StartXSTScript(string psPath)
        {			
            twXSTScript = TextFile.OpenFileReWrite(psPath,"xst.prj");			
        }

        public static void AddXSTScript(string psVerilogFile)
        {
            string sExtention = Utility.StringTakeRightFromEnd(psVerilogFile.Trim(), '.');
            if (sExtention.ToLower() == "v")
                twXSTScript.WriteLine("verilog work " + psVerilogFile);
            else
                if (sExtention.ToLower() == "ucf")
                    sUCF = psVerilogFile;
        }

        public static void CompleteXSTScript()
        {
            twXSTScript.Close();
        }
        
        public static string CopyInterfaceFile(string psSourcePath, string psDestinationPath, string psFileName, bool pbIncludeOnly)
        {
            try
            {
                System.IO.File.Copy(psSourcePath + psFileName, psDestinationPath + psFileName, true);
                if (!pbIncludeOnly)
                    AddXSTScript(psFileName);
                return "";
            }
            catch
            {
                return "Error copying file " + psSourcePath + psFileName + " to " + psDestinationPath + psFileName;
            }
        }

        public static string CopyInterfaceFiles(string psSourcePath, string psDestinationPath)
        {
            System.IO.TextReader oFile;

            try
            {
                oFile = TextFile.OpenFileRead(psSourcePath + "interfacefiles.txt");
            }
            catch
            {
                return "Error opening file " + psSourcePath + "interfacefiles.txt";
            }

            string sFile = oFile.ReadLine();
            string sResult = "";
            while (sFile != null)
            {
                sFile = sFile.Replace("  "," ").Replace("  "," ").Replace("  "," ").Trim();
                if (sFile.Length > 0)
                {
                    bool bInclude = false;
                    if (Utility.StringTakeLeftFromEnd(sFile, ' ').ToLower() == "include")
                    {
                        sFile = Utility.StringTakeRightFromEnd(sFile, ' ');
                        bInclude = true;
                    }
                    sResult = CopyInterfaceFile(psSourcePath, psDestinationPath, sFile,bInclude);
                    if (sResult != "")
                    {
                        oFile.Close();
                        return sResult;
                    }
                }
                sFile = oFile.ReadLine();
            }        
            oFile.Close();
            return "";
        }

        public static string CheckForErrors(string psLogFilePath, string psProgramReference, string psErrorLineStart)
        {
            System.IO.TextReader oFile;
            string sErrors="";
            string sTest = psErrorLineStart.Trim().ToUpper();
            try
            {
                oFile = TextFile.OpenFileRead(psLogFilePath);
            }
            catch
            {
                return "Error opening file " + psLogFilePath;
            }

            string sFile = oFile.ReadLine();
            while (sFile != null)
            {
                string sStart = sFile.Trim().ToUpper();
                if (sStart.Length > sTest.Length)
                    sStart = sStart.Substring(0,sTest.Length);
                if (sStart == sTest)
                    sErrors += sFile.Trim() + "\n";
                sFile = oFile.ReadLine();
            }
            oFile.Close();

            if (sErrors.Length > 0)
                sErrors = psProgramReference + "\n" + sErrors;
            return sErrors;
        }

        public static void UnpackUploadedDirectory(string psFolderPath, string psFilterExtention)
        {
            string sCommand = "tar";
            string sParam = " -xvf " + psFilterExtention + ".o.tar.gz";
            ShellControl.KillAllProcesses("tar");
            ShellControl.StartExe(sCommand, psFolderPath, sParam);
            while (ShellControl.GetProcessCount(sCommand) > 0)
            {
                System.Threading.Thread.Sleep(2000);
            }
        }

        private static void CleanUpXilinx(string psPath, string psTopComponent)
        {
            TextFile.WipeDirectory(psPath, psTopComponent + ".bgn");
            TextFile.WipeDirectory(psPath, psTopComponent + ".bit");
            TextFile.WipeDirectory(psPath, psTopComponent + ".bld");
            TextFile.WipeDirectory(psPath, psTopComponent + ".drc");
            TextFile.WipeDirectory(psPath, psTopComponent + ".lso");
            TextFile.WipeDirectory(psPath, psTopComponent + ".ncd");
            TextFile.WipeDirectory(psPath, psTopComponent + ".ngc");
            TextFile.WipeDirectory(psPath, psTopComponent + ".ngd");
            TextFile.WipeDirectory(psPath, psTopComponent + ".ngr");
            TextFile.WipeDirectory(psPath, psTopComponent + ".pad");
            TextFile.WipeDirectory(psPath, psTopComponent + ".par");
            TextFile.WipeDirectory(psPath, psTopComponent + ".srp");
            TextFile.WipeDirectory(psPath, psTopComponent + ".unroutes");
            TextFile.WipeDirectory(psPath, psTopComponent + ".xpi");
            TextFile.WipeDirectory(psPath, psTopComponent + ".xst");
            TextFile.WipeDirectory(psPath, psTopComponent + "_map.map");
            TextFile.WipeDirectory(psPath, psTopComponent + "_map.mrp");
            TextFile.WipeDirectory(psPath, psTopComponent + "_map.ncd");
            TextFile.WipeDirectory(psPath, psTopComponent + "_map.pcf");
            TextFile.WipeDirectory(psPath, psTopComponent + "_pad.csv");
            TextFile.WipeDirectory(psPath, psTopComponent + "_pad.txt");
            TextFile.WipeDirectory(psPath, psTopComponent + ".gz");
        }

        public static string LaunchXilinxBuild(string psXilinxPath, string psOutputPath, string psTopComponent, string psXilinxChip, bool pbSpeed, string psUCF)
        {
            CleanUpXilinx(psOutputPath,psTopComponent);

            if (!TextFile.FileExists(psXilinxPath + "ise.exe"))
            {
                return "Xilinx ise.exe is not located in '" + psXilinxPath + "', build failed.  Check your settings.";
            }

            Console.WriteLine("Cleaned Up");
            
            System.IO.TextWriter oXST = TextFile.OpenFileReWrite(psOutputPath, psTopComponent + ".xst");
            oXST.WriteLine("run -ifn " + psOutputPath + "xst.prj"); 
            oXST.WriteLine("-ifmt mixed");
            oXST.WriteLine("-top " + psTopComponent);
            oXST.WriteLine("-ofn " + psTopComponent + ".ngc ");
            oXST.WriteLine("-ofmt NGC ");
            oXST.WriteLine("-p " + "xc4vfx12-12-sf363");
            if (pbSpeed)
                oXST.WriteLine("-opt_mode Speed ");
            oXST.WriteLine("-opt_level 2"); // was 1
            oXST.WriteLine("-power NO");
            oXST.WriteLine("-iuc NO");
            //oXST.WriteLine("-lso CFBase.lso");
            oXST.WriteLine("-keep_hierarchy NO");
            oXST.WriteLine("-rtlview Yes");
            oXST.WriteLine("-glob_opt AllClockNets");
            oXST.WriteLine("-read_cores YES");
            oXST.WriteLine("-write_timing_constraints NO");
            oXST.WriteLine("-cross_clock_analysis YES");
            oXST.WriteLine("-hierarchy_separator /");
            oXST.WriteLine("-bus_delimiter <>");
            oXST.WriteLine("-case maintain");
            oXST.WriteLine("-slice_utilization_ratio 100");
            oXST.WriteLine("-bram_utilization_ratio 100");
            oXST.WriteLine("-dsp_utilization_ratio 100");
            oXST.WriteLine("-verilog2001 YES");
            oXST.WriteLine("-fsm_extract YES -fsm_encoding Auto");
            oXST.WriteLine("-safe_implementation no");// was no
            oXST.WriteLine("-fsm_style lut");
            oXST.WriteLine("-ram_extract Yes");
            oXST.WriteLine("-ram_style Auto");
            oXST.WriteLine("-rom_extract Yes");
            oXST.WriteLine("-mux_style Auto");
            oXST.WriteLine("-decoder_extract YES");
            oXST.WriteLine("-priority_extract YES");
            oXST.WriteLine("-shreg_extract YES");
            oXST.WriteLine("-shift_extract YES");
            oXST.WriteLine("-xor_collapse YES");
            oXST.WriteLine("-rom_style Auto");
            oXST.WriteLine("-auto_bram_packing NO");
            oXST.WriteLine("-mux_extract YES");
            oXST.WriteLine("-resource_sharing YES");
            oXST.WriteLine("-async_to_sync NO");
            oXST.WriteLine("-use_dsp48 auto");
            oXST.WriteLine("-iobuf YES");
            oXST.WriteLine("-max_fanout 500");
            oXST.WriteLine("-bufg 32");
            oXST.WriteLine("-bufr 16");
            oXST.WriteLine("-register_duplication YES");
            oXST.WriteLine("-register_balancing No");
            oXST.WriteLine("-slice_packing YES");
            oXST.WriteLine("-optimize_primitives NO");
            oXST.WriteLine("-use_clock_enable Auto");
            oXST.WriteLine("-use_sync_set Auto");
            oXST.WriteLine("-use_sync_reset Auto");
            oXST.WriteLine("-iob auto");
            oXST.WriteLine("-equivalent_register_removal YES");
            oXST.WriteLine("-slice_utilization_ratio_maxmargin 5");
            oXST.Close();

            Console.WriteLine("Part 2");


            string sCommand = "xst";
            string sParam =  " -ifn " + psTopComponent + ".xst";
            ShellControl.KillAllProcesses("xst");
            Console.WriteLine("Part 2.a");
            ShellControl.StartExe(psXilinxPath + sCommand, psOutputPath, sParam);
            Console.WriteLine("Part 2.b");
            if (!Utility.IsUnix())
            {
                while (ShellControl.GetProcessCount(sCommand) > 0)
                {
                    System.Threading.Thread.Sleep(2000);
                }
            }

            // check for xst error
            string sErrors = CheckForErrors(psOutputPath + psTopComponent + ".srp", "xst errors:", "ERROR");
            if (sErrors.Length > 0)
            {
                Console.WriteLine("Failed 1");
                return sErrors;
            }

            if (sUCF.Length == 0)
                sUCF = psUCF;

            Console.WriteLine("Part 3");


            sCommand = "ngdbuild";
            sParam = "-dd _ngo -nt timestamp -uc " + sUCF + " -p " + psXilinxChip + " " + psTopComponent + ".ngc " + psTopComponent + ".ngd";
            ShellControl.StartExe(psXilinxPath + sCommand, psOutputPath, sParam);
            if (!Utility.IsUnix())
            {
                while (ShellControl.GetProcessCount(sCommand) > 0)
                {
                    System.Threading.Thread.Sleep(2000);
                }
            }

            sErrors = CheckForErrors(psOutputPath + psTopComponent + ".bld", "ngdBuild errors:", "ERROR");
            if (sErrors.Length > 0)
                return sErrors;


            sCommand = "map";
            //sParam = "-p " + psXilinxChip + " -cm area -pr b -k 4 -c 100 -o " + psTopComponent + "_map.ncd " + psTopComponent + ".ngd " + psTopComponent + ".pcf";
            sParam = "-p " + psXilinxChip + " -cm area -pr b -c 100 -o " + psTopComponent + "_map.ncd " + psTopComponent + ".ngd " + psTopComponent + ".pcf";

            
            ShellControl.StartExe(psXilinxPath + sCommand, psOutputPath, sParam);
            if (!Utility.IsUnix())
            {
                while (ShellControl.GetProcessCount(sCommand) > 0)
                {
                    System.Threading.Thread.Sleep(2000);
                }
            }

            sErrors = CheckForErrors(psOutputPath + psTopComponent + "_map.mrp", "map errors:", "ERROR");
            if (sErrors.Length > 0)
                return sErrors;

            sCommand = "par";
            sParam = "-w -ol std -t 1 " + psTopComponent + "_map.ncd  " + psTopComponent + ".ncd " + psTopComponent + ".pcf";
            ShellControl.StartExe(psXilinxPath + sCommand, psOutputPath, sParam);
            if (!Utility.IsUnix())
            {
                while (ShellControl.GetProcessCount(sCommand) > 0)
                {
                    System.Threading.Thread.Sleep(2000);
                }
            }

            sErrors = CheckForErrors(psOutputPath + psTopComponent + ".par", "place and route errors:", "ERROR");
            if (sErrors.Length > 0)
                return sErrors;

            sCommand = "bitgen";
            sParam = "-w -g DebugBitstream:No -g Binary:no -g CRC:Enable -g ConfigRate:4 -g CclkPin:PullUp -g M0Pin:PullUp -g M1Pin:PullUp -g M2Pin:PullUp -g ProgPin:PullUp -g DonePin:PullUp -g InitPin:Pullup -g CsPin:Pullup -g DinPin:Pullup -g BusyPin:Pullup -g RdWrPin:Pullup -g TckPin:PullUp -g TdiPin:PullUp -g TdoPin:PullUp -g TmsPin:PullUp -g UnusedPin:PullDown -g UserID:0xFFFFFFFF -g DCIUpdateMode:AsRequired -g StartUpClk:CClk -g DONE_cycle:4 -g GTS_cycle:5 -g GWE_cycle:6 -g LCK_cycle:NoWait -g Match_cycle:Auto -g Security:None -g DonePipe:No -g DriveDone:No -g Encrypt:No " + psTopComponent + ".ncd";
            ShellControl.StartExe(psXilinxPath + sCommand, psOutputPath, sParam);
            if (!Utility.IsUnix())
            {
                while (ShellControl.GetProcessCount(sCommand) > 0)
                {
                    System.Threading.Thread.Sleep(2000);
                }
            }

            sErrors = CheckForErrors(psOutputPath + psTopComponent + ".bgn", "bitgen errors:", "ERROR");
            if (sErrors.Length > 0)
                return sErrors;

            return "";
        }

        public static bool UploadBitFile(string psOutputPath, string psTopComponent)
        {
            string sCommand = "PicoCommand";
            string sParam = "/c " + psTopComponent.ToLower() + ".bit " + TextFile.BuildFileName(psOutputPath, psTopComponent.ToLower() + ".bit");
            ShellControl.StartExe("c:\\pico\\bin\\" + sCommand, psOutputPath, sParam);
            while (ShellControl.GetProcessCount(sCommand) > 0)
            {
                System.Threading.Thread.Sleep(2000);
            }
            
            sParam = "/w " + TextFile.BuildFileName(psOutputPath,psTopComponent.ToLower() + ".bit");
            ShellControl.StartExe("c:\\pico\\bin\\" + sCommand, psOutputPath, sParam);
            while (ShellControl.GetProcessCount(sCommand) > 0)
            {
                System.Threading.Thread.Sleep(1000);
            }

            return true;
        }

        public static string[] LogFileNames(string psTopComponent)
        {
            string[] logFiles = new string[5];

            logFiles[0] = psTopComponent + ".srp";
            logFiles[1] = psTopComponent + ".bld";
            logFiles[2] = psTopComponent + "_map.mrp";
            logFiles[3] = psTopComponent + ".par";
            logFiles[4] = psTopComponent + ".bgn";
            return logFiles;
        }


        public static string LaunchSpectrumBuild(string psSpectrumPath, string psOutputPath)
        {
            string sCommand = "spectrum";
            string sParam = " -file leo.txt";
            ShellControl.KillAllProcesses("spectrum");
            ShellControl.StartExe(psSpectrumPath + sCommand, psOutputPath, sParam);
            if (!Utility.IsUnix())
            {
                while (ShellControl.GetProcessCount(sCommand) > 0)
                {
                    System.Threading.Thread.Sleep(2000);
                }
            }

            string sErrors = TextFile.ReadEntireFile(psOutputPath + "leospec.log");
            if (sErrors.Contains("Error(s) found"))
                return sErrors;

            return "";
        }

        public static string LaunchIcarusSim(string psIcarusPath, string psOutputPath, string psIDCode)
        {
            TextFile.DeleteFile(psOutputPath,"icarus.log");

            string sCommand = "iverilog";
            string sParam = "-o " + psIDCode + " -c " + psIDCode + ".scr";
            ShellControl.KillAllProcesses("iverilog");
            string sErrors = ShellControl.StartExe(psIcarusPath + sCommand, psOutputPath, sParam,true);
            if (!Utility.IsUnix())
            {
                while (ShellControl.GetProcessCount(sCommand) > 0)
                {
                    System.Threading.Thread.Sleep(2000);
                }
            }

            if (sErrors.Length > 0)
                return sErrors;

            sCommand = "vvp";
            sParam = psIDCode + " -l-";
            ShellControl.KillAllProcesses("vvp");
            string sResult = ShellControl.StartExe(psIcarusPath + sCommand, psOutputPath, sParam,true);
            if (!Utility.IsUnix())
            {
                while (ShellControl.GetProcessCount(sCommand) > 0)
                {
                    System.Threading.Thread.Sleep(2000);
                }
            }

            if (sResult.ToUpper().Contains("ERROR:"))
                return sResult;

            return "";
        }

    }
}
